home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 3 / Cream of the Crop 3.iso / comm / wnos5src.zip / PCGEN.ASM < prev    next >
Assembly Source File  |  1993-10-18  |  19KB  |  864 lines

  1. ; Collection of assembler support routines for NOS
  2. ; Copyright 1991 Phil Karn, KA9Q
  3.  
  4.     .MODEL    MEMMOD,C
  5.     LOCALS
  6.     %MACS
  7.     .LALL
  8.     extrn    ctick:proc
  9.     public    eoi
  10.  
  11. ; Hardware vector for timer linkage
  12. ; We use the timer hardware channel here instead of the indirect BIOS
  13. ; channel (1ch) because the latter is sluggish when running under DoubleDos
  14. TIMEVEC    EQU    08h
  15.  
  16.     .DATA
  17.     public    Spsave,Sssave,Intstk,Stktop,Mtasker
  18.     extrn    Isat:word
  19. Spsave    dw    ?            ; Save location for SP during interrupts
  20. Sssave    dw    ?            ; Save location for SS during interrupts
  21. Intstk    dw    512 dup(?)    ; Interrupt working stack
  22. Stktop    equ    $            ; SP set here when entering interrupt
  23. Mtasker    db    ?            ; Type of higher multitasker, if any
  24.  
  25.     .CODE
  26. dbase    dw    @data
  27. jtable    dw    l0,l1,l2,l3,l4,l5,l6,l7,l8,l9,l10,l11,l12,l13,l14,l15
  28. vector    dd    ?            ; place to stash chained vector
  29. vectlo    equ    word ptr vector
  30. vecthi    equ    word ptr vector+2
  31.  
  32. ; Re-arm 8259 interrupt controller(s)
  33. ; Should be called just after taking an interrupt, instead of just
  34. ; before returning. This is because the 8259 inputs are edge triggered, and
  35. ; new interrupts arriving during an interrupt service routine might be missed.
  36. eoi    proc
  37.     cmp    Isat,1
  38.     jnz    @@1                ; Only one 8259, so skip this stuff
  39.     mov    al,0bh            ; read in-service register from
  40.     out    0a0h,al            ; secondary 8259
  41.     nop                    ; settling delay
  42.     nop
  43.     nop
  44.     in    al,0a0h            ; get it
  45.     or    al,al            ; Any bits set?
  46.     jz    @@1                ; nope, not a secondary interrupt
  47.     mov    al,20h            ; Get EOI instruction
  48.     out    0a0h,al            ; Secondary 8259 (PC/AT only)
  49. @@1:    mov    al,20h        ; 8259 end-of-interrupt command
  50.     out    20h,al            ; Primary 8259
  51.     ret
  52. eoi    endp
  53.  
  54. ; common routine for interrupt return
  55. ; Note that all hardware interrupt handlers are expected to return
  56. ; the original vector found when the device first attached. We branch
  57. ; to it just after we've cleaned up here -- this implements shared
  58. ; interrupts through vector chaining. If the original vector isn't
  59. ; available, the interrupt handler must return NULL to avoid a crash!
  60. public    doret
  61.     label    doret    far
  62.     cmp    ax,0            ; is a chained vector present?
  63.     jne    @@1                ; yes
  64.     if    @DataSize NE 0
  65.         cmp    dx,ax
  66.         jne    @@1            ; yes
  67.     endif
  68.     pop    es                ; nope, return directly from interrupt
  69.     pop    di
  70.     pop    si
  71.     pop    bp
  72.     pop    dx
  73.     pop    cx
  74.     pop    bx
  75.     pop    ax
  76.     mov    ss,Sssave
  77.     mov    sp,Spsave        ; restore original stack context
  78.     pop    ds
  79.     iret
  80.  
  81. ; Code to handle vector chaining
  82. @@1:    mov    cs:vectlo,ax    ; stash vector for later branch
  83.     if    @DataSize NE 0
  84.         mov    cs:vecthi,dx
  85.     endif
  86.     pop    es
  87.     pop    di
  88.     pop    si
  89.     pop    bp
  90.     pop    dx
  91.     pop    cx
  92.     pop    bx
  93.     pop    ax
  94.     mov    ss,Sssave
  95.     mov    sp,Spsave        ; restore original stack context
  96.     pop    ds
  97.     if    @DataSize NE 0
  98.         jmp    cs:[vector]    ; jump to the original interrupt handler
  99.     else
  100.         jmp    cs:[vectlo]
  101.     endif
  102.  
  103. ; istate - return current interrupt state
  104.     public    istate
  105. istate    proc
  106.     pushf
  107.     pop    ax
  108.     and    ax,200h
  109.     jz    @@1
  110.     mov ax,1
  111. @@1:    ret
  112. istate    endp
  113.  
  114. ; dirps - disable interrupts and return previous state: 0 = disabled,
  115. ;    1 = enabled
  116.     public dirps
  117. dirps    proc
  118.     pushf                ; save flags on stack
  119.     pop    ax                ; flags -> ax
  120.     and    ax,200h            ; 1<<9 is IF bit
  121.     jz    @@1                ; ints are already off; return 0
  122.     mov    ax,1
  123.     cli                    ; interrupts now off
  124. @@1:    ret
  125. dirps    endp
  126.  
  127. ; restore - restore interrupt state: 0 = off, nonzero = on
  128.     public    restore
  129. restore    proc
  130.     arg is:word
  131.     test    is,0ffffh
  132.     jz    @@1
  133.     sti
  134.     ret
  135. @@1:    cli                ; should already be off, but just in case...
  136.     ret
  137. restore    endp
  138.  
  139.  
  140. ; multitasker types
  141. NONE        equ    0
  142. DOUBLEDOS    equ    1
  143. DESQVIEW    equ    2
  144. WINDOWS3    equ    3
  145. OS2            equ    4
  146.  
  147. ; Relinquish processor so other task can run
  148.     public    giveup
  149. giveup    proc
  150.     pushf                ;save caller's interrupt state
  151.     sti                    ;re-enable interrupts
  152.  
  153.     cmp    Mtasker, OS2
  154.     jnz    @@1
  155.     mov    ax, 1680h
  156. ;    int    2fh
  157.     int 28h
  158.     popf                ; restore interrupts
  159.     ret
  160.  
  161. @@1:    cmp    Mtasker, WINDOWS3
  162.     jnz    @@2
  163.     mov    ax, 1680h
  164.     int    2fh
  165.     cmp    al, 80h            ; call supported?
  166.     jz    @@2                ; nope
  167.     popf                ; yes - restore interrupts
  168.     ret
  169.  
  170. @@2:    cmp    Mtasker, DESQVIEW
  171.     jnz    @@3
  172.     mov    ax, 1000h
  173.     int    15h
  174.     popf                ; restore interrupts
  175.     ret
  176.  
  177. @@3:    cmp    Mtasker, DOUBLEDOS
  178.     jnz    @@4
  179.     mov    al,2            ; 110 ms
  180.     mov    ah,0eeh
  181.     int    21h
  182.     popf                ; restore caller's interrupt state
  183.     ret
  184.  
  185. @@4:    hlt                ; wait for an interrupt
  186.     popf                ; restore interrupts
  187.     ret
  188. giveup    endp
  189.  
  190. ; check for a multitasker running
  191.     public    chktasker
  192. chktasker    proc
  193.     mov    Mtasker,NONE
  194.  
  195.     ; Check for OS/2
  196.     mov    ax,3000h        ; Get MS-DOS Version Number call
  197.     int    21h
  198.     cmp    al,20            ; Version 20 = OS/2 2.0
  199.     jnz @@5
  200.     mov    Mtasker, OS2
  201.     ret
  202.  
  203.       ; Check for Microsoft Windows
  204. @@5:    mov    ax,1600h
  205.  
  206.     ; Check for Microsoft Windows
  207.     mov    ax,1600h
  208.     int    2fh
  209.     cmp    al, 00h            ; 0 means windows multitasking not running
  210.     jz    @@4
  211.     cmp    al, 80h            ; ditto for 80h return
  212.     jz    @@4
  213.     mov    Mtasker, WINDOWS3
  214.     ret
  215.  
  216.     ; Check for DoubleDos
  217. @@4:    mov    ah,0e4h
  218.     int    21h
  219.     cmp    al,1
  220.     jz    @@1
  221.     cmp    al,2
  222.     jnz    @@2
  223. @@1:    mov    Mtasker, DOUBLEDOS
  224.     ret
  225.  
  226.     ; Check for DESQVIEW
  227. @@2:    mov    ax, 2b01h
  228.     mov    cx, 4445h
  229.     mov    dx, 5351h
  230.     int    21h
  231.     cmp    al, 0ffh
  232.     jnz    @@3
  233.     ret
  234.  
  235. @@3:    mov    Mtasker, DESQVIEW
  236.     ret
  237. chktasker    endp
  238.  
  239. ; getss - Read SS for debugging purposes
  240.     public    getss
  241. getss    proc
  242.     mov    ax,ss
  243.     ret
  244. getss    endp
  245.  
  246. ; clockbits - Read low order bits of timer 0 (the TOD clock)
  247. ; This works only for the 8254 chips used in ATs and 386s.
  248. ;
  249. ; The timer runs in mode 3 (square wave mode), counting down
  250. ; by twos, twice for each cycle. So it is necessary to read back the
  251. ; OUTPUT pin to see which half of the cycle we're in. I.e., the OUTPUT
  252. ; pin forms the most significant bit of the count. Unfortunately,
  253. ; the 8253 in the PC/XT lacks a command to read the OUTPUT pin...
  254. ;
  255. ; The PC's clock design is soooo brain damaged...
  256.  
  257.     public    clockbits
  258. clockbits    proc
  259.     mov    al,0c2h            ; latch timer 0 count and status for reading
  260.     pushf
  261.     cli                    ; make chip references atomic
  262.     out    43h,al            ; send latch command
  263.     in    al,40h            ; get status of timer 0
  264.     mov    bl,al            ; save status
  265.     in    al,40h            ; get lsb of count
  266.     mov    ah,al            ; save lsb
  267.     in    al,40h            ; get msb of count
  268.     popf                ; no more chip references
  269.     and    bl,80h            ; we're only interested in the OUT bit
  270.     xchg    ah,al        ; ax = count in correct order
  271.     shr    ax,1            ; count /= 2
  272.     jz    @@3                ; zero count requires carry propagation
  273. @@2:    or    ah,bl        ; combine with OUT bit as most sig bit of count
  274.     ret
  275. @@3:    xor    bl,80h        ; propagate carry by toggling OUT bit when cnt == 0
  276.     or    ah,bl            ; combine with !OUT bit as most sig bit of count
  277.     ret
  278.  
  279. clockbits    endp
  280.  
  281. ; Internet checksum subroutine
  282. ; Compute 1's-complement sum of data buffer
  283. ; Uses an unwound loop inspired by "Duff's Device" for performance
  284. ;
  285. ; Called from C as
  286. ; unsigned short
  287. ; lcsum(buf,cnt)
  288. ; unsigned short *buf;
  289. ; unsigned short cnt;
  290.     public    lcsum
  291. lcsum    proc
  292.     arg    buf:ptr,cnt:word
  293.  
  294.     if    @DataSize NE 0
  295.         uses    ds,si
  296.         lds    si,buf        ; ds:si = buf
  297.     else
  298.         uses    si
  299.         mov    si,buf        ; ds:si = buf (ds already set)
  300.     endif
  301.  
  302.     mov    cx,cnt            ; cx = cnt
  303.     cld                    ; autoincrement si
  304.     mov    ax,cx
  305.     shr    cx,1            ; cx /= 16, number of loop iterations
  306.     shr    cx,1
  307.     shr    cx,1
  308.     shr    cx,1
  309.  
  310.     inc    cx                ; make fencepost adjustment for 1st pass
  311.     and    ax,15            ; ax = number of words modulo 16
  312.     shl    ax,1            ; *=2 for word table index
  313.     lea    bx,jtable        ; bx -> branch table
  314.     add    bx,ax            ; index into jump table
  315.     clc                    ; initialize carry = 0
  316.     mov    dx,0            ; clear accumulated sum
  317.     jmp    word ptr cs:[bx]    ; jump into loop
  318.  
  319. ; Here the real work gets done. The numeric labels on the lodsw instructions
  320. ; are the targets for the indirect jump we just made.
  321. ;
  322. ; Each label corresponds to a possible remainder of (count / 16), while
  323. ; the number of times around the loop is determined by the quotient.
  324. ;
  325. ; The loop iteration count in cx has been incremented by one to adjust for
  326. ; the first pass.
  327. ;
  328. deloop:    lodsw
  329.     adc    dx,ax
  330. l15:    lodsw
  331.     adc    dx,ax
  332. l14:    lodsw
  333.     adc    dx,ax
  334. l13:    lodsw
  335.     adc    dx,ax
  336. l12:    lodsw
  337.     adc    dx,ax
  338. l11:    lodsw
  339.     adc    dx,ax
  340. l10:    lodsw
  341.     adc    dx,ax
  342. l9:    lodsw
  343.     adc    dx,ax
  344. l8:    lodsw
  345.     adc    dx,ax
  346. l7:    lodsw
  347.     adc    dx,ax
  348. l6:    lodsw
  349.     adc    dx,ax
  350. l5:    lodsw
  351.     adc    dx,ax
  352. l4:    lodsw
  353.     adc    dx,ax
  354. l3:    lodsw
  355.     adc    dx,ax
  356. l2:    lodsw
  357.     adc    dx,ax
  358. l1:    lodsw
  359.     adc    dx,ax
  360. l0:    loop    deloop        ; :-)
  361.  
  362.     adc    dx,0            ; get last carries
  363.     adc    dx,0
  364.     mov    ax,dx            ; result into ax
  365.     xchg    al,ah        ; byte swap result (8088 is little-endian)
  366.     ret
  367. lcsum    endp
  368.  
  369. ; Link timer handler into timer chain
  370. ; Arg == address of timer handler routine
  371. ; MUST be called exactly once before uchtimer is called!
  372.  
  373. toff    dw    ?            ; save location for old vector
  374. tseg    dw    ?            ;  must be in code segment
  375.  
  376.     public    chtimer
  377. chtimer    proc
  378.     arg    vec:far ptr
  379.     uses    ds
  380.  
  381.     mov    ah,35h            ; get current vector
  382.     mov    al,TIMEVEC
  383.     int    21h                ; puts vector in es:bx
  384.     mov    cs:tseg,es        ; stash
  385.     mov    cs:toff,bx
  386.  
  387.     mov    ah,25h
  388.     mov    al,TIMEVEC
  389.     lds    dx,vec            ; ds:si = vec
  390.  
  391.     int    21h                ; set new vector
  392.     ret
  393. chtimer    endp
  394.  
  395. ; unchain timer handler from timer chain
  396. ; MUST NOT be called before chtimer!
  397.     public    uchtimer
  398. uchtimer    proc
  399.     uses    ds
  400.  
  401.     mov    ah,25h
  402.     mov    al,TIMEVEC
  403.     mov    dx,toff
  404.     mov    ds,tseg
  405.     int    21h                ; restore old vector
  406.     ret
  407. uchtimer    endp
  408.  
  409. ; Clock tick interrupt handler. Note the use of "label" rather than "proc"
  410. ; here, necessitated by the fact that "proc" automatically generates BP-saving
  411. ; code that we don't want here.
  412.  
  413.     public    btick
  414.     label    btick    far
  415.  
  416.     pushf
  417.     push    ds
  418.     cli
  419.     mov    ds,cs:dbase        ; establish interrupt data segment
  420.  
  421.     mov    Sssave,ss        ; stash user stack context
  422.     mov    Spsave,sp
  423.  
  424.     mov    ss,cs:dbase
  425.     lea    sp,Stktop
  426.  
  427.     push    ax            ; save user regs on interrupt stack
  428.     push    bx
  429.     push    cx
  430.     push    dx
  431.     push    bp
  432.     push    si
  433.     push    di
  434.     push    es
  435.  
  436.     call    ctick
  437.  
  438.      pop    es
  439.     pop    di
  440.     pop    si
  441.     pop    bp
  442.     pop    dx
  443.     pop    cx
  444.     pop    bx
  445.     pop    ax
  446.     mov    ss,Sssave
  447.     mov    sp,Spsave        ; restore original stack context
  448.     pop    ds
  449.     popf
  450.     jmp    dword ptr [toff]    ; link to previous vector
  451.  
  452. ; Convert 32-bit int in network order to host order (dh, dl, ah, al)
  453. ; Called from C as
  454. ; int32 get32(char *cp);
  455.  
  456.     public    get32
  457. get32    proc
  458.     arg    cp:ptr
  459.     if    @DataSize NE 0
  460.         uses    ds,si
  461.         lds    si,cp        ; ds:si = cp
  462.     else
  463.         uses    si
  464.         mov    si,cp        ; ds:si = cp (ds already set)
  465.     endif
  466.  
  467.     cld
  468.     lodsw
  469.     mov    dh,al            ; high word to dx, a-swapping as we go
  470.     mov    dl,ah
  471.     lodsw
  472.     xchg    al,ah        ; low word stays in ax, just swap
  473.     ret
  474. get32    endp
  475.  
  476. ; Convert 16-bit int in network order to host order (ah, al)
  477. ; Called from C as
  478. ; int16 get16(char *cp);
  479.  
  480.     public    get16
  481. get16    proc
  482.     arg    cp:ptr
  483.     if    @DataSize NE 0
  484.         uses    ds,si
  485.         lds    si,cp        ; ds:si = cp
  486.     else
  487.         uses    si
  488.         mov    si,cp        ; ds:si = cp (ds already set)
  489.     endif
  490.  
  491.     lodsw                ; note: direction flag is don't-care
  492.     xchg    al,ah        ; word stays in ax, just swap
  493.     ret
  494. get16    endp
  495.  
  496. ; Convert 32-bit int to network order, returning new pointer
  497. ; Called from C as
  498. ; char *put32(char *cp,int32 x);
  499.  
  500.     public    put32
  501. put32    proc
  502.     arg    cp:ptr,x:dword
  503.     if    @DataSize NE 0
  504.         uses    ds,di
  505.         les    di,cp        ; es:di = cp
  506.         mov    ax,ss        ; our parameter is on the stack, and ds might not
  507.         mov    ds,ax        ; be pointing to ss.
  508.     else
  509.         uses    di
  510.         mov    di,cp        ; es:di = cp
  511.         mov    ax,ds        ; point es at data segment
  512.         mov    es,ax
  513.     endif
  514.  
  515.     cld
  516.     mov    ax,word ptr (x+2)    ; read high word of machine version
  517.     xchg    ah,al        ; swap bytes
  518.     stosw                ; output in network order
  519.     mov    ax,word ptr x    ; read low word of machine version
  520.     xchg    ah,al        ; swap bytes
  521.     stosw                ; put in network order
  522.  
  523.     mov    ax,di            ; return incremented output pointer
  524.     if    @DataSize NE 0
  525.         mov    dx,es        ; upper half of pointer
  526.     endif
  527.     ret
  528. put32    endp
  529.  
  530. ; Convert 16-bit int to network order, returning new pointer
  531. ; Called from C as
  532. ; char *put16(char *cp,int16 x);
  533.  
  534.     public    put16
  535. put16    proc
  536.     arg    cp:ptr,x:word
  537.     uses    di
  538.     if    @DataSize NE 0
  539.         les    di,cp        ;es:di = cp
  540.     else
  541.         mov    di,cp        ; es:di = cp
  542.         mov    ax,ds
  543.         mov    es,ax
  544.     endif
  545.     cld
  546.     mov    ax,x            ; fetch source word in machine order
  547.     xchg    ah,al        ; swap bytes
  548.     stosw                ; save in network order
  549.     mov    ax,di            ; return new output pointer to user
  550.     if    @DataSize NE 0
  551.         mov    dx,es        ; upper half of pointer
  552.     endif
  553.     ret
  554. put16    endp
  555.  
  556. ; kbraw - raw, nonblocking read from console
  557. ; If character is ready, return it; if not, return -1
  558.     public    kbraw
  559. kbraw    proc
  560.     mov    ah,06h            ; Direct Console I/O
  561.     mov    dl,0ffh            ; Read from keyboard
  562.     int    21h                ; Call DOS
  563.     jz    @@1                ; zero flag set -> no character ready
  564.     mov    ah,0            ; valid char is 0-255
  565.     ret
  566. @@1:    mov    ax,-1        ; no char, return -1
  567.     ret
  568. kbraw    endp
  569.  
  570. if    @Cpu AND 2
  571. ; fast I/O buffer routines
  572. ; version for 80186, 286, 386 (uses ins, outs instructions)
  573.  
  574. ; outbuf - put a buffer to an output port
  575.     public    outbuf
  576. outbuf    proc
  577.     arg    port:word,buf:ptr,cnt:word
  578.     if    @DataSize NE 0
  579.         uses    ds,si
  580.         lds    si,buf        ; ds:si = buf
  581.     else
  582.         uses    si
  583.         mov    si,buf        ;ds:si = buf (ds already set)
  584.     endif
  585.     mov    dx,port
  586.     mov    cx,cnt
  587.     cld
  588.     rep outsb            ; works only on PC/AT (80286)
  589.     mov    dx,ds
  590.     mov    ax,si            ; return pointer just past end of buffer
  591.     ret
  592. outbuf    endp
  593.  
  594. ; inbuf - get a buffer from an input port
  595.     public    inbuf
  596. inbuf    proc
  597.     arg    port:word,buf:ptr,cnt:word
  598.     uses    di
  599.     if    @DataSize NE 0
  600.         les    di,buf        ; es:di = buf
  601.     else
  602.         mov    di,buf        ; es:di = buf
  603.         mov    ax,ds
  604.         mov    es,ax
  605.     endif
  606.     mov    dx,port
  607.     mov    cx,cnt
  608.     cld
  609.     rep insb            ; works only on PC/AT (80286)
  610.     mov    dx,es
  611.     mov    ax,di            ; return pointer just past end of buffer
  612.     ret
  613. inbuf    endp
  614.  
  615. else
  616.  
  617. ; fast buffer I/O routines
  618. ; version for 8086/8
  619.  
  620. ; outbuf - put a buffer to an output port
  621.     public    outbuf
  622. outbuf    proc
  623.     arg    port:word,buf:ptr,cnt:word
  624.     if    @DataSize NE 0
  625.         uses    ds,si
  626.         lds    si,buf        ; ds:si = buf
  627.     else
  628.         uses    si
  629.         mov    si,buf        ; ds:si = buf (ds already set)
  630.     endif
  631.  
  632.     mov    dx,port
  633.     mov    cx,cnt
  634.     cld
  635.  
  636. ; If buffer doesn't begin on a word boundary, send the first byte
  637.     test    si,1        ; (buf & 1) ?
  638.     jz    @@even ; no
  639.     lodsb                ; al = *si++;
  640.     out    dx,al            ; out(dx,al);
  641.     dec    cx    ; cx--;
  642.     mov    cnt,cx            ; save for later test
  643. @@even:
  644.     shr    cx,1            ; cx = cnt >> 1; (convert to word count)
  645. ; Do the bulk of the buffer, a word at a time
  646.     jcxz    @@nobuf        ; if(cx != 0){
  647. @@deloop:
  648.     lodsw                ; do { ax = *si++; (si is word pointer)
  649.     out    dx,al            ; out(dx,lowbyte(ax));
  650.     mov    al,ah
  651.     out    dx,al            ; out(dx,hibyte(ax));
  652.     loop    @@deloop    ; } while(--cx != 0); }
  653. ; now check for odd trailing byte
  654. @@nobuf:
  655.     mov    cx,cnt
  656.     test    cx,1
  657.     jz    @@cnteven
  658.     lodsb                ; al = *si++;
  659.     out    dx,al
  660. @@cnteven:
  661.     mov    dx,ds
  662.     mov    ax,si            ; return pointer just past end of buffer
  663.     ret
  664. outbuf    endp
  665.  
  666. ; inbuf - get a buffer from an input port
  667.     public    inbuf
  668. inbuf    proc
  669.     arg port:word,buf:ptr,cnt:word
  670.     uses    di
  671.     if    @DataSize NE 0
  672.         les    di,buf        ; es:di = buf
  673.     else
  674.         mov    di,buf        ; es:di = buf
  675.         mov    ax,ds
  676.         mov    es,ax
  677.     endif
  678.     mov    dx,port
  679.     mov    cx,cnt
  680.     cld
  681.  
  682. ; If buffer doesn't begin on a word boundary, get the first byte
  683.     test    di,1        ; if(buf & 1){
  684.     jz    @@bufeven         ;
  685.     in    al,dx            ; al = in(dx);
  686.     stosb                ; *di++ = al
  687.     dec    cx                ; cx--;
  688.     mov    cnt,cx            ; cnt = cx; } save for later test
  689. @@bufeven:
  690.     shr    cx,1            ; cx = cnt >> 1; (convert to word count)
  691. ; Do the bulk of the buffer, a word at a time
  692.     jcxz    @@nobuf        ; if(cx != 0){
  693. @@deloop:
  694.     in    al,dx            ; do { al = in(dx);
  695.     mov    ah,al
  696.     in    al,dx            ; ah = in(dx);
  697.     xchg    al,ah
  698.     stosw                ; *si++ = ax; (di is word pointer)
  699.     loop    @@deloop    ; } while(--cx != 0);
  700. ; now check for odd trailing byte
  701. @@nobuf:
  702.     mov    cx,cnt
  703.     test    cx,1
  704.     jz    @@cnteven
  705.     in    al,dx
  706.     stosb                ; *di++ = al
  707. @@cnteven:
  708.     mov    dx,es
  709.     mov    ax,di            ; return pointer just past end of buffer
  710.     ret
  711. inbuf    endp
  712.  
  713. endif
  714.  
  715.     public    longdiv
  716.  
  717. ; long unsigned integer division - divide an arbitrary length dividend by
  718. ; a 16-bit divisor. Replaces the dividend with the quotient and returns the
  719. ; remainder. Called from C as
  720. ;
  721. ; unsigned short
  722. ; longdiv(unsigned short divisor,int cnt,unsigned short *dividend);
  723. ;
  724. ;Register usage:
  725. ; di - divisor
  726. ; si - pointer into dividend array
  727. ; cx - loop counter, initialized to the number of 16-bit words in the dividend
  728. ; ax - low word of current dividend before each divide, current quotient after
  729. ; dx - remainder from previous divide carried over, becomes high word of
  730. ;      dividend for next divide
  731.  
  732. longdiv    proc
  733.     arg    divisor:word,cnt:word,dividend:ptr
  734.     if    @DataSize NE 0
  735.         uses    ds,si,di
  736.         lds    si,dividend
  737.     else
  738.         uses    si,di
  739.         mov    si,dividend    ;si -> dividend array
  740.     endif
  741.  
  742.     cmp    divisor,0        ; divisor == 0?
  743.     jne    @2                ; no, ok
  744.     xor    ax,ax            ; yes, avoid divide-by-zero trap
  745.     jmp    short @1
  746.  
  747. @2:    mov    dx,0            ; init remainder = 0
  748.     mov    cx,cnt            ; init cnt
  749.     mov    di,divisor        ; cache divisor in register
  750.  
  751. @@deloop:
  752.     mov    ax,word ptr [si]    ; fetch current word of dividend
  753.     cmp    ax,0            ; dividend == 0 ?
  754.     jne    @7                ; nope, must do division
  755.     cmp    dx,0            ; remainder also == 0?
  756.     je    @4                ; yes, skip division, continue
  757.  
  758. @7:    div    di                ; do division
  759.     mov    word ptr [si],ax    ; save quotient
  760.  
  761. @4:    inc    si                ; next word of dividend
  762.     inc    si
  763.     loop     @@deloop
  764.  
  765.     mov    ax,dx            ; return last remainder
  766. @1:    ret
  767.  
  768. longdiv    endp
  769.  
  770. ; long unsigned integer multiplication - multiply an arbitrary length
  771. ; multiplicand by a 16-bit multiplier, leaving the product in place of
  772. ; the multipler, returning the carry. Called from C as
  773. ;
  774. ; unsigned short
  775. ; longmul(unsigned short multiplier,int cnt,unsigned short *multiplier);
  776. ;
  777. ; Register usage:
  778. ; di = multiplier
  779. ; si = pointer to current word of multiplicand
  780. ; bx = carry from previous round
  781. ; cx = count of words in multiplicand
  782. ; dx,ax = scratch for multiply
  783.  
  784.     public longmul
  785. longmul    proc    far
  786.     arg    multiplier:word,n:word,multiplicand:ptr
  787.     if    @DataSize NE 0
  788.         uses    ds,si,di
  789.         lds    si,multiplicand
  790.     else
  791.         uses    si,di
  792.         mov    si,multiplicand    ; si -> multiplicand array
  793.     endif
  794.  
  795.     mov    di,multiplier    ; cache multiplier in register
  796.     xor    bx,bx            ; init carry = 0
  797.     mov    cx,n            ; fetch n
  798.     mov    ax,cx
  799.     shl    ax,1            ; *2 = word offset
  800.     add    si,ax            ; multiplicand += n
  801.  
  802. @@deloop:
  803.     dec    si
  804.     dec    si                ; work from right to left
  805.     mov    ax,word ptr [si]    ; fetch current multiplicand
  806.     or    ax,ax            ; skip multiply if zero
  807.     jz    @@nomult
  808.     mul    di                ; dx:ax <- ax * di
  809. @@nomult:
  810.     add    ax,bx            ; add carry from previous multiply
  811.     mov    word ptr [si],ax    ; save low order word of product
  812.     mov    bx,0            ; clear previous carry, leaving CF alone
  813.     adc    bx,dx            ; save new carry
  814.     xor    dx,dx            ; clear in case we skip the next mult
  815.     loop    @@deloop
  816.  
  817.     mov    ax,bx            ; return final carry
  818.     ret
  819. longmul    endp
  820.  
  821. ifdef    notdef
  822. ; divide 32 bits by 16 bits, returning both quotient and remainder
  823. ; This allows C programs that need both to avoid having to do two divisions
  824. ;
  825. ; Called from C as
  826. ;    long divrem(dividend,divisor)
  827. ;    long dividend;
  828. ;    short divisor;
  829. ;
  830. ;    The quotient is returned in the low 16 bits of the result,
  831. ;    and the remainder is returned in the high 16 bits.
  832.  
  833.     public    divrem
  834. divrem    proc
  835.     arg    dividend:dword,divisor:word
  836.     mov    ax,word ptr dividend
  837.     mov    dx,word ptr (dividend+2)
  838.     div    divisor
  839.     ret
  840. divrem    endp
  841. endif
  842.  
  843. ifdef    notdef
  844. ; not used at the moment
  845. ; Compute int(log2(x))
  846. ; Called from C as
  847. ; int log2(int16 x);
  848.  
  849.     public    log2
  850. log2    proc
  851.     arg    x:word
  852.     mov    cx,16
  853.     mov    ax,x
  854. @@2:    rcl    ax,1
  855.     jc    @@1
  856.     loop    @@2
  857. @@1:    dec    cx
  858.     mov    ax,cx
  859.     ret
  860. log2    endp
  861. endif
  862.  
  863.     end
  864.